﻿using System;
using System.Collections;
using System.Collections.Generic;
using Sharp3DMath;
using Sharp3DMath.Core;
using Sharp3DMath.Geometry2D;
using System.Text;

namespace Sharp3DMath.Strokes
{
    public enum CurvatureType
    {
        LineFit,//前后方向差
        ABCos,//前后臂余弦
        PointsDis//窗口点距离
    }
    public class StrokeAnalyzer
    {
        #region GetStrokeLength
        public static double GetStrokeLength(Stroke stroke)
        {
            return GetStrokeLength(stroke.Points);
        }
        public static double GetStrokeLength(Vector2F[] stroke)
        {
            double sum = 0.0;
            for (int i = 0; i < stroke.Length - 1; i++)
            {
                sum += DistanceMethods.Distance(stroke[i], stroke[i + 1]);
            }
            return sum;
        }
        public static double GetStrokeLength(List<Vector2F> stroke)
        {
            double sum = 0.0;
            for (int i = 0; i < stroke.Count-1; i++)
            {
                sum += DistanceMethods.Distance(stroke[i], stroke[i + 1]);
            }
            return sum;
        }
        #endregion
        #region ComputeStrokeCurvature
        public static void ComputeStrokeCurvature(Stroke stroke)
        {
            ComputeStrokeCurvature(stroke, CurvatureType.LineFit);
        }
        /// <summary>
        /// 前后臂余弦
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static double[] ComputeCuravture_ABCos(List<Vector2F> data)
        {

            return ComputeCuravture_ABCos(data, 2);
        }
        /// <summary>
        /// 前后臂余弦
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static double[] ComputeCuravture_ABCos(Vector2F[] data)
        {
           return ComputeCuravture_ABCos(data, 2);
        }

        private static double[] ComputeCuravture_ABCos(Vector2F[] data,int dalte)
        {
            double[] curavtures = new double[data.Length];
            for (int i = dalte; i < data.Length - dalte; i++)
            {
                Vector2F v1 = data[i - dalte] - data[i];
                Vector2F v2 = data[i + dalte] - data[i];
                if (v1 == Vector2F.Zero || v2 == Vector2F.Zero)
                {
                    curavtures[i] = 0;
                    continue;
                }

                float dotProduct = Vector2F.DotProduct(v1, v2);

                dotProduct = dotProduct / (v1.GetLength() * v2.GetLength());
                curavtures[i] = dotProduct +1;//取正
            }
            //for (int i = 0; i < dalte; i++)
            //{
            //    curavtures[i] = curavtures[dalte];
            //    curavtures[data.Length - i - 1] = curavtures[data.Length - dalte - 1];
            //}

            Vector2F vt1 = data[0] - data[1];
            Vector2F vt2 = data[2] - data[1];
            if (vt1 == Vector2F.Zero || vt2 == Vector2F.Zero)
            {
                curavtures[1] = 0; curavtures[0] = 0;
            }
            float tdotProduct = Vector2F.DotProduct(vt1, vt2);
            tdotProduct = tdotProduct / (vt1.GetLength() * vt2.GetLength());
            curavtures[1] = curavtures[0] = tdotProduct + 1;

            vt1 = data[data.Length - 1] - data[data.Length - 2];
            vt2 = data[data.Length - 3] - data[data.Length - 2];
            if (vt1 == Vector2F.Zero || vt2 == Vector2F.Zero)
            {
                curavtures[data.Length-2] = 0; curavtures[data.Length-1] = 0;
            }
            tdotProduct = Vector2F.DotProduct(vt1, vt2);
            tdotProduct = tdotProduct / (vt1.GetLength() * vt2.GetLength());
            curavtures[data.Length - 2] = curavtures[data.Length - 1] = tdotProduct + 1;
            return curavtures;
        }
        private static double[] ComputeCuravture_ABCos(List<Vector2F> data, int dalte)
        {
            double[] curavtures = new double[data.Count];
            for (int i = dalte; i < data.Count-dalte; i++)
            {
                Vector2F v1 = data[i - dalte] - data[i];
                Vector2F v2 = data[i + dalte] - data[i];
                if (v1 == Vector2F.Zero||v2==Vector2F.Zero)
                {
                    curavtures[i] = 0;
                    continue;
                }

                float dotProduct = Vector2F.DotProduct(v1, v2);

                dotProduct = dotProduct /(v1.GetLength()*v2.GetLength());
                curavtures[i] = dotProduct + 1;//取正
            }
            for (int i = 0; i < dalte; i++)
            {
                curavtures[i] = curavtures[dalte];
                curavtures[data.Count - i-1] = curavtures[data.Count - dalte-1];
            }
            return curavtures;
        }
        private static void ComputeStrokeCurvature(Stroke stroke,CurvatureType type)
        {
            switch (type)
            {
                #region CurvatureType.DirectionDis
                case CurvatureType.LineFit:
                    {
                            int pcount = stroke.Points.Count;
                            int dircount = stroke.Directions.Count;
                            double curvature;
                            double ddire, ds, ls = 0;

                            if (stroke.Curvatures.Count == 0)
                            {
                                for (int i = 0; i < 2; i++)
                                {
                                    double d2 = stroke.Directions[i + 1];
                                    double d1 = stroke.Directions[i];
                                    ddire = Math.Abs(Math.Abs(d2) - Math.Abs(d1));
                                    if (ddire >= Math.PI)
                                        ddire = ddire - Math.PI;

                                    ds = DistanceMethods.Distance(stroke.Points[i], stroke.Points[i + 1]);
                                    curvature = ddire / ds;
                                    stroke.Curvatures.Add(curvature);
                               }

                                for (int i = 2; i < pcount - 2; i++)
                                {

                                    double d2 = stroke.Directions[i + 2];
                                    double d1 = stroke.Directions[i - 2];



                                    ddire = Math.Abs(Math.Abs(d2) - Math.Abs(d1));
                                    if (ddire >= Math.PI)
                                        ddire = ddire - Math.PI;
                                   


                                    ds = DistanceMethods.Distance(stroke.Points[i - 1], stroke.Points[i + 1]);
                       

                                    curvature = ddire / ds;
                      

                                    stroke.Curvatures.Add(curvature);
                                }
                                stroke.Curvatures.Add(stroke.Curvatures[pcount - 3]);
                                stroke.Curvatures.Add(stroke.Curvatures[pcount - 2]);
                            }
                    }
                    break;
                #endregion
                #region CurvatureType.ABCos
                case CurvatureType.ABCos:
                    break;
                #endregion
                #region CurvatureType.PointsDis
                case CurvatureType.PointsDis:
                    break;
                #endregion
                default:
                    break;
            }
           
        }
        #endregion
        #region StrokeCenter
        public static Vector2F StrokeCenter(Stroke stroke)
        {
            return MathEx.Center(stroke.Points.ToArray());
        }
        
        #endregion
        public static Vector2F[] StrokeApprox(Vector2F[] stroke,int distance)
        {
            List<Vector2F> temp = new List<Vector2F>();
           // Vector2F bias = new Vector2F(0, 10);
            for (int i = 0; i < stroke.Length; i = i + distance)
            {
                temp.Add(stroke[i]);
            }
            temp.Add(stroke[stroke.Length-1]);
            return temp.ToArray();
        }

        public static void SegmentizeSquiggle(ArrayList vertexIndices, Vector2F[] points, int iHead, int iTail, double tolerance)
        {
            int index = iHead;
            int num2 = iTail;
            while (index < iTail)
            {
                int num3 = index;
                double num4 = 0.0;
                for (int i = index; i < num2; i++)
                {
                    double num6 = DistanceMethods.Distance(points[i], points[index], points[num2]);
                    if (num6 > num4)
                    {
                        num4 = num6;
                        num3 = i;
                    }
                }
                if (num4 < tolerance)
                {
                    vertexIndices.Add(num2);
                    index = num2;
                    num2 = iTail;
                }
                else
                {
                    num2 = num3;
                }
            }
        }

        public static Vector2F[] SegmentizeStroke(Stroke stroke, double tolerance, out int[] indices)
        {
            Vector2F[] points = stroke.Points.ToArray();
            int length = points.Length;
            ArrayList vertexIndices = new ArrayList();
            vertexIndices.Add(0);
            SegmentizeSquiggle(vertexIndices, points, 0, length - 1, tolerance);
            indices = vertexIndices.ToArray(typeof(int)) as int[];
            int num2 = indices.Length;
            Vector2F[] pointArray2 = new Vector2F[num2];
            for (int i = 0; i < num2; i++)
            {
                int index = indices[i];
                pointArray2[i] = points[index];
            }
            return pointArray2;
        }

        public static Vector2F[] RemoveNearPoints(Vector2F[] points,double tolerance)
        {
            System.Diagnostics.Debug.Assert(points.Length > 1);
            Vector2F point = points[0];
            List<Vector2F> returnPoints = new List<Vector2F>();
            returnPoints.Add(point);

            for (int i = 1; i < points.Length; i++)
            {
                if (DistanceMethods.Distance(point,points[i])<tolerance)
                {
                    
                    point.X= (point.X + points[i].X) / 2;
                    point.Y = (point.Y + points[i].Y) / 2;
                    returnPoints.RemoveAt(returnPoints.Count - 1);
                    returnPoints.Add(point);

                }
                else
                {
                    returnPoints.Add(points[i]);
                    point = points[i];
                }
            }
            return returnPoints.ToArray();
        }

        public static int OrderDetected(Vector2F s,Vector2F m,Vector2F e)
        {
            Vector2F vect1 = m - s;
            Vector2F vect2 = e - s;

            float temp = Vector2F.KrossProduct(vect1, vect2);
            if (MathEx.ApproxEquals(temp,0))
            {
                return 0;
            }
            if (temp>0)
            {
                return 1;
            }
            if (temp<0)
            {
                return -1;
            }
            return 0; 
        }
       
    }
}
